﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Data;
using System.Text;
using System.IO;
using System.Web;
using System.Xml;
using Microsoft.SharePoint;

namespace SharePointPowerToys
{
    public class CamlSPSiteDataQueryDataProvider : IPowerQueryDataProviderInterface
    {
        #region properties

        public string SiteUrl { get; set; }
        public string ListName { get; set; }
        public bool DebugMode  { get; set; }
        public long ItemLimit { get; set; }
        public NameValueCollection Parameters  { get; set; }
        public NameValueCollection WebParameters  { get; set; }

        #endregion

        #region GetXmlReader
        public XmlReader GetXmlReader()
        {
            MemoryStream ms = new MemoryStream();
            XmlTextWriter xml = new XmlTextWriter(ms, Encoding.UTF8);

            xml.Formatting = Formatting.Indented;
            xml.WriteStartDocument();

            //bool debugMode = false;
            //string debugModeParam = StringGet(_dataQueryProviderParams["DebugMode"]);
            //if ((debugModeParam == "1") || (debugModeParam.ToLower() == "true"))
            //{
            //    debugMode = true;
            //}
            string sourceWeb = SiteUrl;// SPContext.Current.Web.Url;
            string sourceLists = ListName; // "Pages";
            string sourceSiteFolder = string.Empty;
            string webs = string.Empty;
            string itemLimit = string.Empty;
            string viewFields = string.Empty;
            string xmlContainer = "items";
            string xmlItem = "item";
            List<string> tokenArray = new List<string>();

            string camlSiteUrl = Helpers.StringGet(Parameters["camlSiteUrl"], SPContext.Current.Web.Url);
            string camlListName = Helpers.StringGet(Parameters["camlListName"], "Style Library");
            string camlFolderName = Helpers.StringGet(Parameters["camlFolderName"], "CAML");
            string camlFileName = Helpers.StringGet(Parameters["camlFileName"], Helpers.FileNameGet(HttpContext.Current.Request.PathInfo));

            if (String.IsNullOrEmpty(sourceWeb))
            {
                sourceWeb = SPContext.Current.Web.Url;
            }

            if (ItemLimit > 0)
            {
                itemLimit = ItemLimit.ToString();
            }
            if (DebugMode)
            {
                xml.WriteElementString("sourceWeb", sourceWeb);
                xml.WriteElementString("sourceLists", sourceLists);
                xml.WriteElementString("ItemLimit", ItemLimit.ToString());
                xml.WriteElementString("camlSiteUrl", camlSiteUrl);
                xml.WriteElementString("camlListName", camlListName);
                xml.WriteElementString("camlFolderName", camlFolderName);
                xml.WriteElementString("camlFileName", camlFileName);
                xml.WriteElementString("PathInfo", HttpContext.Current.Request.PathInfo);
            }

            string caml = Helpers.StringGet(Parameters["caml"], string.Empty);

            // Let WebPartParams override CAML file, so don't even read file
            if (caml == string.Empty && camlFileName != string.Empty)
            {
                caml = Helpers.GetSpFile(camlSiteUrl, camlListName, camlFolderName, camlFileName);
            }

            string defaultCaml = Helpers.StringGet(Parameters["DefaultCAML"], string.Empty);

            //Check for DefaultCAML on WebPart versus CAML from File.
            if ((caml != string.Empty) || (defaultCaml != string.Empty))
            {
                caml = Helpers.StringGet(caml, defaultCaml);
            }

            //Update CAML with Tokens
            string tokens = Helpers.StringGet(Parameters["Tokens"], string.Empty);
            string tokenDelimiter = Helpers.StringGet(Parameters["TokenDelimiter"], ",");

            if (tokens != string.Empty)
            {
                tokenArray.AddRange(tokens.Split(tokenDelimiter.ToCharArray()));

                foreach (string token in tokenArray)
                {
                    int splitLoc = token.IndexOf("=");
                    caml = caml.Replace(token.Substring(0, splitLoc), token.Substring(splitLoc + 1, token.Length - (splitLoc + 1)));
                }
            }

            //Set Settings to Defaults from CAML
            if (caml != string.Empty)
            {
                sourceWeb = Helpers.TagGet(caml, "SourceSiteUrl", sourceWeb);
                caml = Helpers.TagContentsRemove(caml, "SourceSiteUrl");

                sourceLists = Helpers.TagGet(caml, "SourceListName", sourceLists);
                caml = Helpers.TagContentsRemove(caml, "SourceListName");

                sourceSiteFolder = Helpers.TagGet(caml, "SourceSiteFolder", string.Empty);
                caml = Helpers.TagContentsRemove(caml, "SourceSiteFolder");

                viewFields = Helpers.TagGet(caml, "viewFields", viewFields);
                caml = Helpers.TagContentsRemove(caml, "viewFields");

                webs = Helpers.TagGet(caml, "webs", webs);
                caml = Helpers.TagContentsRemove(caml, "webs");

                //itemLimit = TagGet(caml, "rowLimit", itemLimit);
                //caml = TagContentsRemove(caml, "rowLimit");

                xmlContainer = Helpers.TagGet(caml, "xmlContainer", xmlContainer);
                caml = Helpers.TagContentsRemove(caml, "xmlContainer");

                xmlItem = Helpers.TagGet(caml, "xmlItem", xmlItem);
                caml = Helpers.TagContentsRemove(caml, "xmlItem");
            }

            // Override CAML Settings with WebPartParamas
            sourceWeb = Helpers.StringGet(Parameters["SourceWeb"], sourceWeb);
            sourceLists = Helpers.StringGet(Parameters["SourceLists"], sourceLists);
            sourceSiteFolder = Helpers.StringGet(Parameters["SourceFolder"], sourceSiteFolder);
            viewFields = Helpers.StringGet(Parameters["ViewFields"], viewFields);
            webs = Helpers.StringGet(Parameters["webs"], webs);
            itemLimit = Helpers.StringGet(Parameters["ItemLimit"], itemLimit);
            xmlContainer = Helpers.StringGet(Parameters["xmlContainer"], xmlContainer);
            xmlItem = Helpers.StringGet(Parameters["xmlItem"], xmlItem);

            if (String.IsNullOrEmpty(sourceWeb) && SPContext.Current != null && SPContext.Current.Web != null)
            {
                sourceWeb = SPContext.Current.Web.Url.ToString();
            }
            //Since ViewFields is not working yet, use DisplayFields to filter
            string displayFields = viewFields;

            if (displayFields != string.Empty)
            {
                displayFields = displayFields.ToLower().Replace("<fieldref name=", string.Empty);
                displayFields = displayFields.ToLower().Replace("\"", string.Empty);
                displayFields = displayFields.ToLower().Replace("'", string.Empty);
                displayFields = displayFields.ToLower().Replace("/>", ",");
                displayFields = displayFields.Replace("\r\n ", "");
                displayFields = displayFields.Replace("\r\n", "");
                displayFields = displayFields.Replace(", ", ",");
            }
            displayFields = Helpers.StringGet(Parameters["DisplayFields"], displayFields);

            List<string> displayFieldsArray = new List<string>();

            if (displayFields != string.Empty)
            {
                if (tokenArray.Count > 0)
                {
                    foreach (string token in tokenArray)
                    {
                        int splitLoc = token.IndexOf("=");
                        string tokenName = token.Substring(0, splitLoc);
                        string tokenValue = token.Substring(splitLoc + 1, token.Length - (splitLoc + 1));
                        if (tokenValue.StartsWith("{"))
                        {
                            string paramName = tokenValue.Substring(1, tokenValue.Length - 2);
                            string paramValue = string.Empty;
                            if (Parameters[paramName] != null)
                            {
                                paramValue = Parameters[paramName];
                            }
                            if (paramValue != String.Empty)
                            {
                                tokenValue = paramValue;
                            }
                        }
                        displayFields = displayFields.Replace(tokenName, tokenValue);
                    }
                }

                displayFieldsArray.AddRange(displayFields.Split(','));
            }

            // Check for * DisplayFields or ViewFields

            // Check for Debug Mode and Append Settings to Xml
            if (DebugMode)
            {
                xml.WriteElementString("SourceSiteUrl", sourceWeb);
                xml.WriteElementString("SourceListName", sourceLists);
                xml.WriteElementString("sourceSiteFolder", sourceSiteFolder);
                xml.WriteElementString("Query", caml);
                xml.WriteElementString("viewFields", viewFields);
                xml.WriteElementString("displayFields", displayFields);
                xml.WriteElementString("webs", webs);
                xml.WriteElementString("rowLimit", itemLimit);
                xml.WriteElementString("xmlContainer", xmlContainer);
                xml.WriteElementString("xmlItem", xmlItem);
            }
            SPSecurity.RunWithElevatedPrivileges(delegate()
            {
                try
                {
                    using (SPSite site = new SPSite(sourceWeb))
                    {
                        using (SPWeb web = site.OpenWeb())
                        {
                            //SPDocumentLibrary list = (SPDocumentLibrary)web.Lists[sourceListName];
                            //SPList list = web.Lists[sourceListName];
                            SPSiteDataQuery q = new SPSiteDataQuery();

                            //Set SPSiteDataQuery.Query = <Where> + <OrderBy>
                            if (caml != string.Empty)
                            {
                                q.Query = Helpers.TagGet(caml, "Where", string.Empty, true) +
                                          Helpers.TagGet(caml, "OrderBy", string.Empty, true);
                            }

                            //Set SPSiteDataQuery.ViewFields (doesn't appear tow work yet)
                            if (viewFields != string.Empty)
                            {
                                q.ViewFields = viewFields;
                            }

                            //Set SPSiteDataQuery.RowLimit
                            if (itemLimit != string.Empty)
                            {
                                int rowLimitValue;
                                if (Int32.TryParse(itemLimit, out rowLimitValue))
                                {
                                    q.RowLimit = (uint)rowLimitValue;
                                }
                            }

                            //Set SPSiteDataQuery.Webs
                            if (webs != string.Empty)
                            {
                                q.Webs = webs;
                            }
                           
                            try
                            {
                                xml.WriteStartElement(xmlContainer);
                                
                                //Execute Query and populate it into the XmlReader
                                DataTable table = web.GetSiteData(q);
                                foreach (DataRow item in table.Rows)
                                {
                                    xml.WriteStartElement(xmlItem);
                                    try
                                    {
                                        foreach (DataColumn field in table.Columns)
                                        {
                                            if ((displayFields == string.Empty) || (displayFieldsArray.Contains(field.ColumnName.ToLower())))
                                            {
                                                string fieldValue = string.Empty;
                                                switch (field.GetType().ToString())
                                                {
                                                    case "Microsoft.SharePoint.SPFieldLookup":
                                                        //fieldValue = Helpers.FieldLookupValueGet(item, item.Field<DataColumn>("InternalName").ToString()));
                                                        break;
                                                    case "Microsoft.SharePoint.SPFieldDateTime":
                                                        DateTime fieldDateTime;
                                                        //fieldValue = Helpers.FieldValueGet(item, field.InternalName);
                                                        if (DateTime.TryParse(fieldValue, out fieldDateTime))
                                                        {
                                                            fieldValue = fieldDateTime.ToString("s");
                                                        }
                                                        break;
                                                    //case "Microsoft.SharePoint.SPFieldMultiLineText":
                                                    //    //xml.WriteElementString("RichTextMode", ((SPFieldMultiLineText)field).RichTextMode.ToString());
                                                    //    if (((SPFieldMultiLineText)field).RichTextMode != SPRichTextMode.FullHtml)
                                                    //    {
                                                    //        fieldValue = FieldValueGet(item, field.InternalName);
                                                    //    } else
                                                    //    {
                                                    //        if (item != null)
                                                    //        {
                                                    //            fieldValue = field.GetFieldValueAsHtml(item[field.InternalName]);
                                                    //        }
                                                    //    }
                                                    //    break;
                                                    default:
                                                        //fieldValue = Helpers.FieldValueGet(item, field.InternalName);
                                                        fieldValue = Helpers.StringGet(item[field.ColumnName]);
                                                        break;
                                                }
                                                xml.WriteElementString(field.ColumnName, fieldValue);
                                            }
                                        }
                                    }
                                    catch (Exception ex)
                                    {
                                        xml.WriteElementString("error", ex.Message);
                                    }
                                    finally
                                    {
                                        xml.WriteEndElement();
                                    }
                                }
                            }
                            catch (Exception ex)
                            {
                                xml.WriteElementString("error", ex.Message);
                            }
                            xml.WriteEndElement(); //end Items wrapper tag
                        }
                    }
                }
                catch (Exception ex)
                {
                    xml.WriteStartElement("errors");
                    xml.WriteElementString("Error", ex.Message);
                    xml.WriteEndElement();
                }
            });

            xml.WriteEndDocument();

            xml.Flush();
            ms.Position = 0;
            //try
            //{
            return XmlReader.Create(ms);
            //}
            //catch (Exception ex)
            //{
            //MemoryStream msEx = new MemoryStream();
            //XmlTextWriter xmlEx = new XmlTextWriter(msEx, Encoding.UTF8);

            //xmlEx.Formatting = Formatting.Indented;
            //xmlEx.WriteStartDocument();
            //xmlEx.WriteStartElement("errors");
            //xmlEx.WriteElementString("error", ex.Message);
            //xmlEx.WriteEndElement();
            //xmlEx.WriteEndDocument();

            //xmlEx.Flush();
            //msEx.Position = 0;

            //return XmlReader.Create(msEx);
            //}
        }
        #endregion


    }
}
